import time
import json
from umqtt.simple import MQTTClient
import hmac
from hashlib import sha256
from config import ALIYUN_PRODUCT_KEY, ALIYUN_DEVICE_NAME, ALIYUN_DEVICE_SECRET, ALIYUN_REGION_ID, ALIYUN_SECURE_MODE, TOPIC_DOWN, ALIYUN_KEEPALIVE_INTERVAL

class AliyunMQTT:
    def __init__(self, on_message_callback, ntp_manager):
        self.product_key = ALIYUN_PRODUCT_KEY
        self.device_name = ALIYUN_DEVICE_NAME
        self.device_secret = ALIYUN_DEVICE_SECRET
        self.region_id = ALIYUN_REGION_ID or "cn-shanghai"
        self.secure_mode = ALIYUN_SECURE_MODE  # 2=非加密，3=加密
        self.sign_method = "hmacsha256"
        self.server = f"{self.product_key}.iot-as-mqtt.{self.region_id}.aliyuncs.com"
        self.port = 1883 if self.secure_mode == 2 else 8883
        self.connected = False
        self.client = None
        self.last_ping_time = 0
        self.keepalive = ALIYUN_KEEPALIVE_INTERVAL
        self.last_activity = 0
        self.on_message_callback = on_message_callback
        self.timestamp_ms = ntp_manager.get_utc_timestamp_ms()
        self.client_id = self._generate_client_id()
        self.username, self.password = self._generate_auth_params()
    def _generate_client_id(self):
        """生成正确的ClientID（含UTC时间戳）"""
        # 格式：productKey.deviceName|securemode=?,signmethod=hmacsha256,timestamp=?|
        return f"{self.product_key}.{self.device_name}|securemode={self.secure_mode},signmethod={self.sign_method},timestamp={self.timestamp_ms}|"

    def _generate_auth_params(self):
        """生成正确的Username和Password（用UTC时间戳签名）"""
        username = f"{self.device_name}&{self.product_key}"
        timestamp_str = str(self.timestamp_ms)  # 阿里云要求时间戳为字符串格式

        # 签名源字符串（顺序严格：clientId、deviceName、productKey、timestamp）
        sign_str = (
            f"clientId{self.product_key}.{self.device_name}"
            f"deviceName{self.device_name}"
            f"productKey{self.product_key}"
            f"timestamp{timestamp_str}"
        )

        # HMAC-SHA256签名（与signmethod一致）
        sign = hmac.new(
            self.device_secret.encode(),
            sign_str.encode(),
            sha256
        ).hexdigest()

        # Password就是纯签名
        return username, sign

    def connect(self):
        """连接阿里云IoT平台"""
        try:
            # 打印参数用于调试
            print(f"连接参数:")
            print(f"Server: {self.server}:{self.port}")
            print(f"ClientID: {self.client_id}")
            print(f"Username: {self.username}")
            print(f"Password: {self.password}")  # 注意：实际部署时建议注释此行
            
            # 初始化MQTT客户端（根据secure_mode决定是否启用SSL）
            self.client = MQTTClient(
                client_id=self.client_id,
                server=self.server,
                port=self.port,
                user=self.username,
                password=self.password,
                keepalive=60,
                ssl=(self.secure_mode == 3)  # 只有secure_mode=3时启用SSL
            )
            
            # 设置消息回调
            self.client.set_callback(self._on_message)
            
            # 建立连接
            self.client.connect()
            self.last_ping_time = time.time()
            self.last_activity = time.time()
            self.connected = True
            
            # 订阅设备指令主题
            self.client.subscribe(TOPIC_DOWN)
            print(f"已订阅指令主题: {TOPIC_DOWN}")
            
            return True
        except Exception as e:
            print(f"连接阿里云IoT平台失败: {str(e)}")
            self.connected = False
            return False

    def _on_message(self, topic, msg):
        """内部消息处理，转发给外部回调"""
        try:
            message = msg.decode()
            print(f"收到消息: 主题={topic}, 内容={message}")
            if self.on_message_callback:
                self.on_message_callback(topic, message)
        except Exception as e:
            print(f"消息处理错误: {e}")

    def publish(self, topic, data):
        """发布消息到阿里云IoT平台"""
        if not self.connected or not self.client:
            print("未连接到阿里云IoT平台，无法发布消息")
            return False
            
        try:
            if isinstance(data, dict):
                data = json.dumps(data)
            self.client.publish(topic, data)
            self.last_activity = time.time()
            return True
        except Exception as e:
            print(f"消息发布失败: {e}")
            return False

    def check_ping(self):
        # 检查是否需要发送ping
        current_time = time.time()
        # 如果距离上次活动后过了keepalive时间，则发送ping
        if current_time - self.last_activity >= self.keepalive:
            print("发送心跳ping")
            self.client.ping()
            self.last_ping_time = current_time
            self.last_activity = current_time  # 更新活动时间
    def check_msg(self):
        """检查是否有新消息（非阻塞）"""
        if self.connected and self.client:
            try:
                self.client.check_msg()
            except Exception as e:
                print(f"检查消息错误: {e}")
                self.connected = False

    def wait_msg(self):
        """阻塞式等待消息"""
        if self.connected and self.client:
            try:
                return self.client.wait_msg()
            except Exception as e:
                print(f"等待消息错误: {e}")
                self.connected = False

    def disconnect(self):
        """断开连接"""
        if self.connected and self.client:
            try:
                self.client.disconnect()
            except Exception as e:
                print(f"断开连接错误: {e}")
            finally:
                self.connected = False
